#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

// 常量定义（保持不变）
#define PROXY_PREFIX "https://ghproxy.cn/"
#define GITHUB_BASE "https://github.com/"
#define REPO_SUFFIX ".git"
#define GITHUB_API_BASE "https://api.github.com/repos/"
#define USER_AGENT "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"

/**
 * libcurl 回调函数：将HTTP响应写入内存缓冲区
 * @param ptr: 响应数据指针
 * @param size: 每个数据块大小
 * @param nmemb: 数据块数量
 * @param userdata: 自定义缓冲区（这里是 char* 类型的内存块）
 * @return: 实际写入的字节数
 */
static size_t write_to_memory(void *ptr, size_t size, size_t nmemb, void *userdata)
{
  size_t total_size = size * nmemb;
  char **buffer = (char **)userdata;
  char *temp = realloc(*buffer, strlen(*buffer) + total_size + 1); // 扩容

  if (!temp)
  {
    fprintf(stderr, "内存分配失败（write_to_memory）\n");
    return 0; // 返回0表示写入失败，libcurl会终止请求
  }

  *buffer = temp;
  strncat(*buffer, (char *)ptr, total_size); // 拼接新数据
  return total_size;
}

/**
 * libcurl 回调函数：将HTTP响应写入文件
 * @param ptr: 响应数据指针
 * @param size: 每个数据块大小
 * @param nmemb: 数据块数量
 * @param userdata: 文件指针（FILE*）
 * @return: 实际写入的字节数
 */
static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata)
{
  // size_t total_size = size * nmemb;
  FILE *file = (FILE *)userdata;
  return fwrite(ptr, size, nmemb, file); // 直接写入文件
}

/**
 * 初始化 libcurl（全局只需要初始化一次）
 * @return: 0=成功，1=失败
 */
int init_curl()
{
  CURLcode code = curl_global_init(CURL_GLOBAL_DEFAULT);
  if (code != CURLE_OK)
  {
    fprintf(stderr, "libcurl初始化失败: %s\n", curl_easy_strerror(code));
    return 1;
  }
  return 0;
}

/**
 * 发送HTTP GET请求，将响应保存到内存
 * @param url: 请求URL
 * @param response: 输出参数，用于存储响应内容（需提前初始化空字符串）
 * @return: 0=成功，1=失败
 */
int curl_get_to_memory(const char *url, char **response)
{
  CURL *curl = curl_easy_init();
  if (!curl)
  {
    fprintf(stderr, "curl_easy_init 失败\n");
    return 1;
  }

  // 初始化响应缓冲区（空字符串）
  *response = calloc(1, 1);
  if (!*response)
  {
    perror("calloc failed");
    curl_easy_cleanup(curl);
    return 1;
  }

  // 配置curl选项
  curl_easy_setopt(curl, CURLOPT_URL, url);                       // 请求URL
  curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);          // 设置User-Agent（避免GitHub 403）
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);             // 跟随重定向
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_memory); // 响应写入回调
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);            // 回调的用户数据（内存缓冲区）
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);             // 验证SSL证书（安全起见）
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);             // 验证主机名

  // 执行请求
  CURLcode code = curl_easy_perform(curl);
  if (code != CURLE_OK)
  {
    fprintf(stderr, "HTTP请求失败: %s (URL: %s)\n", curl_easy_strerror(code), url);
    free(*response);
    *response = NULL;
    curl_easy_cleanup(curl);
    return 1;
  }

  // 检查HTTP响应码（200=成功）
  long http_code = 0;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
  if (http_code != 200)
  {
    fprintf(stderr, "HTTP响应码错误: %ld (URL: %s)\n", http_code, url);
    free(*response);
    *response = NULL;
    curl_easy_cleanup(curl);
    return 1;
  }

  // 清理资源
  curl_easy_cleanup(curl);
  return 0;
}

/**
 * 发送HTTP GET请求，将响应保存到文件
 * @param url: 请求URL
 * @param filename: 输出文件名
 * @return: 0=成功，1=失败
 */
int curl_get_to_file(const char *url, const char *filename)
{
  CURL *curl = curl_easy_init();
  if (!curl)
  {
    fprintf(stderr, "curl_easy_init 失败\n");
    return 1;
  }

  // 打开文件（二进制模式，覆盖写入）
  FILE *file = fopen(filename, "wb");
  if (!file)
  {
    perror("fopen failed");
    curl_easy_cleanup(curl);
    return 1;
  }

  // 配置curl选项
  curl_easy_setopt(curl, CURLOPT_URL, url);                     // 请求URL
  curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);        // 设置User-Agent
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);           // 跟随重定向
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_file); // 响应写入回调
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);              // 回调的用户数据（文件指针）
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);           // 验证SSL证书
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);           // 验证主机名
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);               // 显示进度（可选）
  curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, NULL);       // 使用默认进度显示

  // 执行请求
  printf("正在下载: %s -> %s\n", url, filename);
  CURLcode code = curl_easy_perform(curl);
  if (code != CURLE_OK)
  {
    fprintf(stderr, "下载失败: %s (URL: %s)\n", curl_easy_strerror(code), url);
    fclose(file);
    curl_easy_cleanup(curl);
    return 1;
  }

  // 检查HTTP响应码
  long http_code = 0;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
  if (http_code != 200)
  {
    fprintf(stderr, "HTTP响应码错误: %ld (URL: %s)\n", http_code, url);
    fclose(file);
    remove(filename); // 删除错误文件
    curl_easy_cleanup(curl);
    return 1;
  }

  // 清理资源
  fclose(file);
  curl_easy_cleanup(curl);
  printf("下载成功: %s\n", filename);
  return 0;
}

/**
 * 代理克隆仓库（git clone 仍依赖外部git命令，但HTTP请求已用libcurl验证URL可达性）
 */
int proxy_git_clone(const char *owner, const char *repo)
{
  if (!owner || !repo || !*owner || !*repo)
  {
    fprintf(stderr, "错误: 仓库所有者和名称不能为空\n");
    return 1;
  }

  // 1. 构建代理URL并验证可达性（用libcurl发送HEAD请求，避免完整下载）
  char proxy_url[512];
  snprintf(proxy_url, sizeof(proxy_url), "%s%s%s/%s%s",
           PROXY_PREFIX, GITHUB_BASE, owner, repo, REPO_SUFFIX);

  CURL *curl = curl_easy_init();
  if (!curl)
  {
    fprintf(stderr, "curl_easy_init 失败\n");
    return 1;
  }

  // 配置HEAD请求（仅验证URL是否存在）
  curl_easy_setopt(curl, CURLOPT_URL, proxy_url);
  curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
  curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // HEAD请求（不下载正文）
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);

  // 验证URL可达性
  printf("正在验证代理URL可达性: %s\n", proxy_url);
  CURLcode code = curl_easy_perform(curl);
  long http_code = 0;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
  curl_easy_cleanup(curl);

  if (code != CURLE_OK || (http_code != 200 && http_code != 401))
  {
    // GitHub仓库可能返回401（需要认证），但git clone仍可匿名访问，故允许401
    fprintf(stderr, "警告: URL验证失败，但仍尝试克隆（可能是git专用协议）\n");
  }

  // 2. 执行git clone（此处仍依赖外部git命令，因git协议非HTTP，libcurl无法替代）
  char git_cmd[1024];
  snprintf(git_cmd, sizeof(git_cmd), "git clone \"%s\"", proxy_url);
  printf("执行命令: %s\n", git_cmd);

  int result = system(git_cmd); // 此处保留system()，因git无官方C库，替代成本极高
  if (result != 0)
  {
    fprintf(stderr, "错误: 克隆仓库失败\n");
    return 3;
  }

  printf("仓库克隆成功\n");
  return 0;
}

/**
 * 代理下载GitHub Release文件（完全用libcurl实现）
 */
int proxy_download(const char *owner, const char *repo, const char *file_path)
{
  if (!owner || !repo || !file_path || !*owner || !*repo || !*file_path)
  {
    fprintf(stderr, "错误: 仓库所有者、名称和文件路径不能为空\n");
    return 1;
  }

  // 1. 构建原始URL和代理URL
  char original_url[512];
  snprintf(original_url, sizeof(original_url), "%s%s/%s/releases/download/%s",
           GITHUB_BASE, owner, repo, file_path);

  char proxy_url[1024];
  snprintf(proxy_url, sizeof(proxy_url), "%s%s", PROXY_PREFIX, original_url);

  // 2. 提取文件名（处理Linux路径分隔符'/'）
  const char *filename = strrchr(file_path, '/');
  filename = filename ? (filename + 1) : file_path;

  // 3. 用libcurl下载到文件
  return curl_get_to_file(proxy_url, filename) ? 3 : 0;
}

/**
 * 保存GitHub API响应到文件（用libcurl实现）
 */
int save_api_response(const char *url, const char *filename)
{
  // 1. 用libcurl获取API响应到内存
  char *response = NULL;
  if (curl_get_to_memory(url, &response) != 0)
  {
    fprintf(stderr, "API请求失败: %s\n", url);
    return 0;
  }

  // 2. 将内存中的响应写入文件
  FILE *file = fopen(filename, "w");
  if (!file)
  {
    perror("fopen failed");
    free(response);
    return 0;
  }

  fputs(response, file);
  fclose(file);
  free(response);

  printf("API响应已保存到: %s\n", filename);
  return 1;
}

// ------------------- 以下函数逻辑未变，仅保留 -------------------
void parse_and_print_files(const char *json_data)
{
  if (!json_data)
  {
    fprintf(stderr, "错误: 没有JSON数据可解析\n");
    return;
  }

  const char *assets_start = strstr(json_data, "\"assets\":[");
  if (!assets_start)
  {
    printf("未找到资产列表，请检查API响应\n");
    return;
  }
  assets_start += strlen("\"assets\":[");

  const char *assets_end = strstr(assets_start, "]");
  if (!assets_end)
  {
    printf("未找到资产列表的结束位置\n");
    return;
  }

  printf("找到的文件列表:\n");
  const char *ptr = assets_start;
  int count = 1;

  while (ptr < assets_end && (ptr = strstr(ptr, "\"name\":")) != NULL)
  {
    ptr += strlen("\"name\":");
    while (ptr < assets_end && isspace((unsigned char)*ptr))
      ptr++;

    if (ptr < assets_end && *ptr == '"')
    {
      ptr++;
      const char *name_start = ptr;
      while (ptr < assets_end && *ptr != '"')
        ptr++;

      if (ptr < assets_end && *ptr == '"')
      {
        printf("%d. %.*s\n", count++, (int)(ptr - name_start), name_start);
      }
    }
    ptr++;
  }

  if (count == 1)
  {
    printf("在资产列表中未找到任何文件\n");
  }
}

void parse_and_print_tags(const char *json_data)
{
  if (!json_data)
  {
    fprintf(stderr, "错误: 没有JSON数据可解析\n");
    return;
  }

  const char *array_start = strstr(json_data, "[");
  const char *array_end = strstr(json_data, "]");

  if (!array_start || !array_end || array_start >= array_end)
  {
    printf("未找到有效的tags数组\n");
    return;
  }

  printf("找到的tags列表:\n");
  const char *ptr = array_start;
  int count = 1;

  while (ptr < array_end && (ptr = strstr(ptr, "\"name\":")) != NULL)
  {
    ptr += strlen("\"name\":");
    while (ptr < array_end && isspace((unsigned char)*ptr))
      ptr++;

    if (ptr < array_end && *ptr == '"')
    {
      ptr++;
      const char *name_start = ptr;
      while (ptr < array_end && *ptr != '"')
        ptr++;

      if (ptr < array_end && *ptr == '"')
      {
        printf("%d. %.*s\n", count++, (int)(ptr - name_start), name_start);
      }
    }
    ptr++;
  }

  if (count == 1)
  {
    printf("未找到任何tags\n");
  }
}
int list_release_files(const char *owner, const char *repo, const char *release_tag)
{
  if (!owner || !repo || !release_tag || !*owner || !*repo || !*release_tag)
  {
    fprintf(stderr, "错误: 仓库所有者、名称和release标签不能为空\n");
    return 1;
  }

  // 1. 构建GitHub API URL（查询指定tag的release信息）
  char api_url[512];
  snprintf(api_url, sizeof(api_url), "%s%s/%s/releases/tags/%s",
           GITHUB_API_BASE, owner, repo, release_tag);
  printf("正在查询API: %s\n", api_url);

  // 2. 用libcurl获取API响应到内存
  char *json_data = NULL;
  if (curl_get_to_memory(api_url, &json_data) != 0)
  {
    fprintf(stderr, "错误: 获取release信息失败\n");
    return 3;
  }

  // 3. 将响应保存到临时文件
  const char *temp_file = "github_release.tmp.json";
  FILE *file = fopen(temp_file, "w");
  if (!file)
  {
    perror("fopen failed");
    free(json_data);
    return 4;
  }
  fputs(json_data, file);
  fclose(file);

  // 4. 解析并打印release文件列表
  parse_and_print_files(json_data);
  printf("完整API响应已保存到: %s\n", temp_file);

  // 5. 释放资源
  free(json_data);
  return 0;
}

int list_tags(const char *owner, const char *repo)
{
  if (!owner || !repo || !*owner || !*repo)
  {
    fprintf(stderr, "错误: 仓库所有者和名称不能为空\n");
    return 1;
  }

  // 1. 构建GitHub API URL（查询仓库所有tags）
  char api_url[512];
  snprintf(api_url, sizeof(api_url), "%s%s/%s/tags",
           GITHUB_API_BASE, owner, repo);
  printf("正在查询API: %s\n", api_url);

  // 2. 用libcurl获取API响应到内存
  char *json_data = NULL;
  if (curl_get_to_memory(api_url, &json_data) != 0)
  {
    fprintf(stderr, "错误: 获取tags信息失败\n");
    return 3;
  }

  // 3. 将响应保存到临时文件
  const char *temp_file = "github_tags.tmp.json";
  FILE *file = fopen(temp_file, "w");
  if (!file)
  {
    perror("fopen failed");
    free(json_data);
    return 4;
  }
  fputs(json_data, file);
  fclose(file);

  // 4. 解析并打印tags列表
  parse_and_print_tags(json_data);
  printf("完整API响应已保存到: %s\n", temp_file);

  // 5. 释放资源
  free(json_data);
  return 0;
}

void print_usage(const char *program_name)
{
  fprintf(stderr, "用法: %s <命令> [参数...]\n", program_name);
  fprintf(stderr, "命令说明:\n");
  fprintf(stderr, "  clone    <owner> <repo>          - 通过代理克隆GitHub仓库（依赖git命令）\n");
  fprintf(stderr, "  download <owner> <repo> <path>   - 通过代理下载GitHub Release文件（libcurl实现）\n");
  fprintf(stderr, "  list     <owner> <repo> <tag>    - 列出指定tag的Release文件（libcurl实现）\n");
  fprintf(stderr, "  tags     <owner> <repo>          - 列出仓库所有tags（libcurl实现）\n");
  fprintf(stderr, "\n示例:\n");
  fprintf(stderr, "  %s clone tsoding markut\n", program_name);
  fprintf(stderr, "  %s tags erlang otp\n", program_name);
  fprintf(stderr, "  %s list erlang otp OTP-28.0.2\n", program_name);
  fprintf(stderr, "  %s download erlang otp OTP-28.0.2/otp_win64_28.0.2.exe\n", program_name);
}

int main(int argc, char *argv[])
{
  // 1. 初始化libcurl（全局唯一初始化）
  if (init_curl() != 0)
  {
    return 1;
  }

  // 2. 检查命令行参数
  if (argc < 2)
  {
    print_usage(argv[0]);
    curl_global_cleanup();
    return 1;
  }

  int result = 1;
  const char *cmd = argv[1];

  // 3. 解析命令并执行
  if (strcmp(cmd, "clone") == 0)
  {
    if (argc != 4)
    {
      fprintf(stderr, "错误: clone命令需2个参数: <owner> <repo>\n");
      print_usage(argv[0]);
    }
    else
    {
      result = proxy_git_clone(argv[2], argv[3]);
    }
  }
  else if (strcmp(cmd, "download") == 0)
  {
    if (argc != 5)
    {
      fprintf(stderr, "错误: download命令需3个参数: <owner> <repo> <file_path>\n");
      print_usage(argv[0]);
    }
    else
    {
      result = proxy_download(argv[2], argv[3], argv[4]);
    }
  }
  else if (strcmp(cmd, "list") == 0)
  {
    if (argc != 5)
    {
      fprintf(stderr, "错误: list命令需3个参数: <owner> <repo> <release_tag>\n");
      print_usage(argv[0]);
    }
    else
    {
      result = list_release_files(argv[2], argv[3], argv[4]);
    }
  }
  else if (strcmp(cmd, "tags") == 0)
  {
    if (argc != 4)
    {
      fprintf(stderr, "错误: tags命令需2个参数: <owner> <repo>\n");
      print_usage(argv[0]);
    }
    else
    {
      result = list_tags(argv[2], argv[3]);
    }
  }
  else
  {
    fprintf(stderr, "错误: 未知命令: %s\n", cmd);
    print_usage(argv[0]);
  }

  // 4. 清理libcurl资源
  curl_global_cleanup();
  return result;
}
